include( "shared.lua" )

local dlightenabled = CreateClientConVar("portal_dynamic_light", "0", true) --Kinda laggy, default it to off
local lightteleport = CreateClientConVar("portal_light_teleport", "0", true)
local bordersenabled = CreateClientConVar("portal_borders", "1", true)
local betabordersenabled = CreateClientConVar("portal_beta_borders", "1", true)

local texFSB = render.GetSuperFPTex() -- I'm really not sure if I should even be using these D:
local texFSB2 = render.GetSuperFPTex2()

 -- Make our own material to use, so we aren't messing with other effects.
local PortalMaterial = CreateMaterial(
		"PortalMaterial",
		"GMODScreenspace",
		{
			[ '$basetexture' ] = texFSB,
			//[ '$basetexturetransform' ] = "center .5 .5 scale 1 1 rotate 0 translate 0 0",
			[ '$texturealpha' ] = "0",
			[ '$vertexalpha' ] = "1",
		}
	)

// rendergroup
ENT.RenderGroup = RENDERGROUP_BOTH

/*------------------------------------
	Initialize()
------------------------------------*/
function ENT:Initialize( )

	self:SetRenderBounds( self:OBBMins()*20, self:OBBMaxs()*20 )
	
	self.openpercent = 0
	
end

usermessage.Hook("Portal:Moved", function(umsg)
	local ent = umsg:ReadEntity()
	if ent and ent:IsValid() and ent.openpercent then
		ent.openpercent = 0
	end
end)

--I think this is from sassilization..
local function IsInFront( posA, posB, normal )

	local Vec1 = ( posB - posA ):GetNormalized()

	return ( normal:Dot( Vec1 ) < 0 )

end

function ENT:Think()

	if self:GetNWBool("Potal:Activated",false) == false then return end
	
	self.openpercent = math.Approach( self.openpercent, 1, FrameTime() * 2 * ( 1 - self.openpercent + 0.25 ) )

	if dlightenabled:GetBool() == false then return end
	
	local portaltype = self:GetNWInt("Potal:PortalType",TYPE_BLUE)

	local glowcolor = Color( 64, 144, 255, 255 )
	
	if portaltype == TYPE_ORANGE then
		glowcolor = Color( 255, 160, 32, 255 )
	end
	
	--[[if lightteleport:GetBool() then
	
		local portal = self:GetNWEntity( "Potal:Other", nil )
	
		if IsValid( portal ) then

			glowvec = render.GetLightColor( portal:GetPos() ) * 255
			glowcolor = Color( glowvec.x, glowvec.y, glowvec.z )
			
		end
			
	end]]
	
	local dlight = DynamicLight( self:EntIndex() )
	if dlight then
		local col = glowcolor
		dlight.Pos = self:GetPos() + self:GetAngles():Forward()
		dlight.r = col.r
		dlight.g = col.g
		dlight.b = col.b
		dlight.Brightness = 2
		dlight.Decay = 256
		dlight.Size = self.openpercent * 256
		dlight.DieTime = CurTime() + 0.1
	end
end

local nonlinkedblue = surface.GetTextureID( "sprites/noblue" )
local nonlinkedorange = surface.GetTextureID( "sprites/nored" )
local bluebordermat = surface.GetTextureID( "sprites/blborder" )
local orangebordermat = surface.GetTextureID( "sprites/ogborder" )
local bluebetabordermat = surface.GetTextureID( "sprites/blueborder" )
local orangebetabordermat = surface.GetTextureID( "sprites/redborder" )

function ENT:DrawPortalEffects( portaltype )

	local ang = self:GetAngles()
	
	local res = 0.1
	
	local percentopen = self.openpercent
	
	local width = ( percentopen ) * 105
	local height = ( percentopen ) * 114
	
	if betabordersenabled:GetBool() then
		height = ( percentopen ) * 112
	end
	
	ang:RotateAroundAxis( ang:Right(), -90 )
	ang:RotateAroundAxis( ang:Up(), 90 )
	
	local origin = self:GetPos() + ( self:GetForward() * 0.1 ) - ( self:GetUp() * height / -2 ) - ( self:GetRight() * width / -2 )
	
	cam.Start3D2D( origin, ang, res )
	
		surface.SetDrawColor( 255, 255, 255, 255 )
	
		if ( RENDERING_PORTAL or RENDERING_MIRROR or !self:GetNWBool( "Potal:Linked", false ) ) then
		
			if portaltype == TYPE_BLUE then
			
				surface.SetTexture( nonlinkedblue )
				
			elseif portaltype == TYPE_ORANGE then
			
				surface.SetTexture( nonlinkedorange )
				
			end
			
			surface.DrawTexturedRect( 0, 0, width / res , height / res )
			
		end
		
		if !self:GetNWBool( "Potal:Linked", false ) then
		
			if portaltype == TYPE_BLUE then
			
				surface.SetTexture( nonlinkedblue )
				
			elseif portaltype == TYPE_ORANGE then
			
				surface.SetTexture( nonlinkedorange )
				
			end
			
			surface.DrawTexturedRect( 0, 0, width / res , height / res )
			
		end
		
		if bordersenabled:GetBool() == true then
			if portaltype == TYPE_BLUE then
				
				if betabordersenabled:GetBool() then
				
					surface.SetTexture( bluebetabordermat )
					
				else
				
					surface.SetTexture( bluebordermat )
					
				end
				
				surface.DrawTexturedRect( 0, 0, width / res , height / res )
				
			elseif portaltype == TYPE_ORANGE then
				
				if betabordersenabled:GetBool() then
				
					surface.SetTexture( orangebetabordermat )
					
				else
				
					surface.SetTexture( orangebordermat )
					
				end
				
				surface.DrawTexturedRect( 0, 0, width / res , height / res )
				
			end
			
		end
		
	cam.End3D2D()
	
end

function ENT:Draw()

	local viewent = GetViewEntity()
	local pos = ( IsValid( viewent ) and viewent != LocalPlayer() ) and GetViewEntity():GetPos() or EyePos()

	if IsInFront( pos, self:GetPos(), self:GetForward() ) and self:GetNWBool("Potal:Activated",false) then

		local portaltype = self:GetNWInt( "Potal:PortalType",TYPE_BLUE )

		render.ClearStencil() -- Make sure the stencil buffer is all zeroes before we begin
		render.SetStencilEnable( true )
		render.SetStencilFailOperation( STENCILOPERATION_KEEP )
		render.SetStencilZFailOperation( STENCILOPERATION_KEEP )  -- Don't change anything if the pixel is occoludded (so we don't see things thru walls)
		render.SetStencilPassOperation( STENCILOPERATION_REPLACE ) -- Replace the value of the buffer's pixel with the reference value
		render.SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_ALWAYS ) -- Always replace regardless of whatever is in the stencil buffer currently

		render.SetStencilReferenceValue( 1 )
		
		local percentopen = self.openpercent
		self:SetModelScale( Vector( percentopen, percentopen, percentopen ) )
		self:DrawModel()
		
		render.SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_EQUAL )
		render.SetStencilPassOperation( STENCILOPERATION_REPLACE )
		
		render.SetStencilReferenceValue( 1 )
		
		local ToRT = portaltype == TYPE_BLUE and texFSB or texFSB2
	
		PortalMaterial:SetMaterialTexture( "$basetexture", ToRT )
		render.SetMaterial( PortalMaterial )
		render.DrawScreenQuad()
		
		render.SetStencilEnable( false )
		
		self:DrawPortalEffects( portaltype )
	
	end

end

function ENT:RenderPortal( origin, angles, numtimes )

	local portal = self:GetNWEntity( "Potal:Other", nil )

	if IsValid( portal ) and self:GetNWBool( "Potal:Linked", false ) and self:GetNWBool( "Potal:Activated", false ) then
	
		local portaltype = self:GetNWInt( "Potal:PortalType", TYPE_BLUE )
		
		local normal = self:GetForward()
		local distance = normal:Dot( self:GetPos() )
		
		local othernormal = portal:GetForward()
		local otherdistance = othernormal:Dot( portal:GetPos() )
		
		// quick access
		local forward = angles:Forward()
		local up = angles:Up()
		
		// reflect origin
		local dot = origin:DotProduct( normal ) - distance
		origin = origin + ( -2 * dot ) * normal
		
		// reflect forward
		local dot = forward:DotProduct( normal )
		forward = forward + ( -2 * dot ) * normal
		
		// reflect up		
		local dot = up:DotProduct( normal )
		up = up + ( -2 * dot ) * normal
		
		// convert to angles
		angles = math.VectorAngles( forward, up )
		
		local LocalOrigin = self:WorldToLocal( origin )
		local LocalAngles = self:WorldToLocalAngles( angles )
		
		// repair
		LocalOrigin.y = -LocalOrigin.y
		LocalAngles.y = -LocalAngles.y
		LocalAngles.r = -LocalAngles.r
		
		local view = {}
		view.x = 0
		view.y = 0
		view.w = ScrW()
		view.h = ScrH()
		view.origin = portal:LocalToWorld( LocalOrigin )
		view.angles = portal:LocalToWorldAngles( LocalAngles )
		view.drawhud = false
		view.drawviewmodel = false
		
		local oldrt = render.GetRenderTarget()
		
		local ToRT = portaltype == TYPE_BLUE and texFSB or texFSB2
		
		render.SetRenderTarget( ToRT )
			
			render.PushCustomClipPlane( othernormal, otherdistance )

				render.Clear( 0, 0, 0, 255 )
				render.ClearDepth()
				render.ClearStencil()

				portal:SetNoDraw( true )
					RENDERING_PORTAL = true
						render.RenderView( view )
						render.UpdateScreenEffectTexture()
					RENDERING_PORTAL = false
				portal:SetNoDraw( false )

			render.PopCustomClipPlane()
			
		render.SetRenderTarget( oldrt )
		
	end

end

/*------------------------------------
	ShouldDrawLocalPlayer()
------------------------------------*/
--Draw yourself into the portal.. YES YOU CAN SEE YOURSELF! (Bug? Can't see your weapons)
hook.Add( "ShouldDrawLocalPlayer", "Portal.ShouldDrawLocalPlayer", function()
	if RENDERING_PORTAL then
		return true
	end
end )

hook.Add( "RenderScene", "Portal.RenderScene", function( Origin, Angles )
	// render each portal
	for k, v in ipairs( ents.FindByClass( "prop_portal" ) ) do
		local viewent = GetViewEntity()
		local pos = ( IsValid( viewent ) and viewent != LocalPlayer() ) and GetViewEntity():GetPos() or Origin
		if IsInFront( pos, v:GetPos(), v:GetForward() ) then --if the player is in front of the portal, then render it..
			// call into it to render
			v:RenderPortal( Origin, Angles )
		end
	
	end
end )

/*------------------------------------
	GetMotionBlurValues()
------------------------------------*/
hook.Add( "GetMotionBlurValues", "Portal.GetMotionBlurValues", function( x, y, fwd, spin )
	if RENDERING_PORTAL then
		return 0, 0, 0, 0
	end
end )

hook.Add( "PostProcessPermitted", "Portal.PostProcessPermitted", function( element )
	if element == "bloom" and RENDERING_PORTAL then
		return false
	end
end )

usermessage.Hook( "Portal:ObjectInPortal", function(umsg)
	local portal = umsg:ReadEntity()
	local ent = umsg:ReadEntity()
	if IsValid( ent ) and IsValid( portal ) then
		ent.InPortal = portal
	end
end )

usermessage.Hook( "Portal:ObjectLeftPortal", function(umsg)
	local ent = umsg:ReadEntity()
	if IsValid( ent ) then
		ent.InPortal = false
		ent:SetRenderClipPlaneEnabled(false)
	end
end )

hook.Add( "RenderScreenspaceEffects", "Portal.RenderScreenspaceEffects", function()
	for k,v in pairs( ents.GetAll() ) do
		if IsValid( v.InPortal ) then
			--local plane = Plane(v.InPortal:GetForward(),v.InPortal:GetPos())
			
			local normal = v.InPortal:GetForward()
			local distance = normal:Dot( v.InPortal:GetPos() )
			
			v:SetRenderClipPlaneEnabled( true )
			v:SetRenderClipPlane( normal, distance )
		end
	end
end )

/*------------------------------------
	VectorAngles()
------------------------------------*/
function math.VectorAngles( forward, up )

	local angles = Angle( 0, 0, 0 )

	local left = up:Cross( forward )
	left:Normalize()
	
	local xydist = math.sqrt( forward.x * forward.x + forward.y * forward.y )
	
	// enough here to get angles?
	if( xydist > 0.001 ) then
	
		angles.y = math.deg( math.atan2( forward.y, forward.x ) )
		angles.p = math.deg( math.atan2( -forward.z, xydist ) )
		angles.r = math.deg( math.atan2( left.z, ( left.y * forward.x ) - ( left.x * forward.y ) ) )

	else
	
		angles.y = math.deg( math.atan2( -left.x, left.y ) )
		angles.p = math.deg( math.atan2( -forward.z, xydist ) )
		angles.r = 0
	
	end

	return angles
	
end